package com.sojson.util.file.interfaces.impl;

import static com.sojson.util.file.FileUtil.THIS_LOCALITY_DIRECTORY_BUCKET_NAME_BASE;
import static com.sojson.util.file.FileUtil.THIS_LOCALITY_FILE_SYSTEM_BUCKET_NAME_BASE;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.NoSuchAlgorithmException;
import java.util.Properties;

import org.apache.commons.io.IOUtils;
import org.csource.common.MyException;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.FileInfo;
import org.csource.fastdfs.ProtoCommon;
import org.csource.fastdfs.StorageClient;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;
import org.springframework.core.io.ClassPathResource;

import com.sojson.constant.Constant;
import com.sojson.constant.ConstantByMap;
import com.sojson.util.StringUtil;
import com.sojson.util.file.FileUtil;
import com.sojson.util.file.interfaces.FileSystemInterface;
import com.sojson.util.properties.PropertiesUtil;

import cn.hutool.core.convert.Convert;
import lombok.extern.slf4j.Slf4j;

/**
 * FastDFS文件系统工具类
 * 
 * @author liu
 * @Date 2020-06-15 14:45:43
 * @Description
 *
 */
@Slf4j
public class FastDfsUtilImpl implements FileSystemInterface {

    private static FastDfsUtilImpl fastDFSUtilsImpl;
    private static final TrackerClient TRACKER = new TrackerClient();
    private static CustomStorageClient STORAGE_CLIENT;
    /** FastDFS的服务器的协议 */
    private static final String AGREEMENT =
        Convert.toStr(Constant.PROPERTIES.getProperty("so.fastdfs.agreement"), "http://");
    /** FastDFS的服务器的地址 */
    private static final String URL = Constant.PROPERTIES.getProperty("so.fastdfs.url");
    /** FastDFS的服务器加密的密钥 */
    private String secretKey =
        Convert.toStr(Constant.PROPERTIES.getProperty("so.fastdfs.http_secret_key"), "SojsonFastDFSToken");

    private FastDfsUtilImpl() {}

    static {
        init();
    }

    /**
     * 静态代码块不能用return
     */
    private static void init() {
        TrackerServer trackerServer = null;
        StorageServer storageServer = null;
        try {
            // 用main方法的时候调用
            // ClientGlobal.init("environment/constant/fdfs-client.properties");
            // 用容器的时候调用
            String property = Constant.PROPERTIES.getProperty("so.filename.fdfsClient");
            if (StringUtil.isBlankString(property)) {
                log.warn("FastDFS初始化异常,fdfs-client.properties不存在!");
                return;
            }
            ClassPathResource classPathResource = new ClassPathResource(property);
            if (!classPathResource.exists()) {
                log.warn("FastDFS初始化异常,fdfs-client.properties不存在!");
                return;
            }
            Properties properties = PropertiesUtil.getProperties(classPathResource);
            ClientGlobal.initByProperties(properties);
            TrackerClient tracker = new TrackerClient();
            trackerServer = tracker.getConnection();
            STORAGE_CLIENT = getInstances().new CustomStorageClient(trackerServer, storageServer);
        } catch (Exception e) {
            log.error("FastDFS初始化异常," + e.getMessage());
        }
    }

    /**
     * 将本地文件上传到服务器
     * 
     * @param filePath 本地文件路径
     * @return 
     * @throws Exception
     */
    @Override
    public String upload(String filePath) throws Exception {
        return uploading(THIS_LOCALITY_FILE_SYSTEM_BUCKET_NAME_BASE, FileUtil.getFileSuffixByPath(filePath), filePath);
    }

    /**
     * 将本地文件上传到服务器
     * 
     * @param fileName 文件名
     * @param filePath 本地文件路径
     * @return 
     * @throws Exception
     */
    @Override
    public String uploading(String fileName, String filePath) throws Exception {
        return uploading(THIS_LOCALITY_FILE_SYSTEM_BUCKET_NAME_BASE, FileUtil.getFileSuffix(fileName), filePath);
    }

    /**
     * 将本地文件上传到服务器
     * 
     * @param fileSuffix 文件后缀
     * @param filePath 本地文件路径
     * @return 
     * @throws Exception
     */
    @Override
    public String uploadingBySuffix(String fileSuffix, String filePath) throws Exception {
        return uploading(THIS_LOCALITY_FILE_SYSTEM_BUCKET_NAME_BASE, fileSuffix, filePath);
    }

    /**
     * 将本地文件上传到服务器
     * 
     * @param bucketName 服务器文件路径(一级目录)
     * @param filePath 本地文件路径
     * @return 
     * @throws Exception
     */
    @Override
    public String upload(String bucketName, String filePath) throws Exception {
        return uploading(bucketName, FileUtil.getFileSuffixByPath(filePath), filePath);
    }

    /**
     * 将本地文件上传到服务器
     * 
     * @param bucketName 服务器文件路径(一级目录)
     * @param fileSuffix 文件后缀
     * @param filePath 本地文件路径
     * @return
     * @throws Exception
     */
    @Override
    public String uploading(String bucketName, String fileSuffix, String filePath) throws Exception {
        return STORAGE_CLIENT.upload(bucketName, filePath, fileSuffix, null)[1];
    }

    /**
     * 将本地文件上传到服务器
     * 
     * @param bucketName 服务器文件路径(一级目录)
     * @param fileName 文件名
     * @param filePath 本地文件路径
     * @return
     * @throws Exception
     */
    @Override
    public String upload(String bucketName, String fileName, String filePath) throws Exception {
        return uploading(bucketName, FileUtil.getFileSuffix(fileName), filePath);
    }

    /**
     * 将文件流上传到服务器
     * 
     * @param bytes 文件的字节流
     * @param fileSuffix 文件后缀
     * @return 
     * @throws Exception
     */
    @Override
    public String uploading(byte[] bytes, String fileSuffix) throws Exception {
        return uploading(bytes, fileSuffix, THIS_LOCALITY_FILE_SYSTEM_BUCKET_NAME_BASE);
    }

    /**
     * 将文件流上传到服务器
     * 
     * @param bytes 文件的字节流
     * @param fileName 文件名
     * @return 
     * @throws Exception
     */
    @Override
    public String upload(byte[] bytes, String fileName) throws Exception {
        return uploading(bytes, FileUtil.getFileSuffix(fileName), THIS_LOCALITY_FILE_SYSTEM_BUCKET_NAME_BASE);
    }

    /**
     * 将文件流上传到服务器
     * 
     * @param bytes 文件的字节流
     * @param fileSuffix 文件后缀
     * @param bucketName 服务器文件路径(一级目录)
     * @return
     * @throws Exception
     */
    @Override
    public String uploading(byte[] bytes, String fileSuffix, String bucketName) throws Exception {
        String[] fileIds = null;
        fileIds = STORAGE_CLIENT.upload_file(bucketName, bytes, fileSuffix, null);
        // System.out.println(fileIds.length);
        // System.out.println("组名:" + fileIds[0]);
        // System.out.println("路径: " + fileIds[1]);
        return fileIds[1];
    }

    /**
     * 将文件流上传到服务器
     * 
     * @param bytes 文件的字节流
     * @param fileName 文件名
     * @param bucketName 服务器文件路径(一级目录)
     * @return
     * @throws Exception
     */
    @Override
    public String upload(byte[] bytes, String fileName, String bucketName) throws Exception {
        return uploading(bytes, FileUtil.getFileSuffix(fileName), bucketName);
    }

    /**
     * 将文件流上传到服务器
     * 
     * @param inputStream 文件的输入流
     * @param fileSuffix 文件后缀
     * @return 
     * @throws Exception
     */
    @Override
    public String uploading(InputStream inputStream, String fileSuffix) throws Exception {
        return uploading(inputStream, fileSuffix, THIS_LOCALITY_FILE_SYSTEM_BUCKET_NAME_BASE);
    }

    /**
     * 将文件流上传到服务器
     * 
     * @param inputStream 文件的输入流
     * @param fileName 文件名
     * @return 
     * @throws Exception
     */
    @Override
    public String upload(InputStream inputStream, String fileName) throws Exception {
        return uploading(inputStream, FileUtil.getFileSuffix(fileName), THIS_LOCALITY_FILE_SYSTEM_BUCKET_NAME_BASE);
    }

    /**
     * 将文件流上传到服务器
     * 
     * @param inputStream 文件的输入流
     * @param fileSuffix 文件后缀
     * @param bucketName 服务器文件路径(一级目录)
     * @return
     * @throws Exception
     */
    @Override
    public String uploading(InputStream inputStream, String fileSuffix, String bucketName) throws Exception {
        String uploading = null;
        try {
            uploading = uploading(IOUtils.toByteArray(inputStream), fileSuffix, bucketName);
        } finally {
            FileUtil.closeStream(inputStream);
        }
        return uploading;
    }

    /**
     * 将文件流上传到服务器
     * 
     * @param inputStream 文件的输入流
     * @param fileName 文件名
     * @param bucketName 服务器文件路径(一级目录)
     * @return
     * @throws Exception
     */
    @Override
    public String upload(InputStream inputStream, String fileName, String bucketName) throws Exception {
        return uploading(inputStream, FileUtil.getFileSuffix(fileName), bucketName);
    }

    /**
     * 根据Key删除文件
     * 
     * @param key 密钥(上传后返回的字符串)
     * @return 成功还是失败(true:成功,false:失败)
     * @throws Exception
     */
    @Override
    public boolean remove(String key) throws Exception {
        return remove(THIS_LOCALITY_FILE_SYSTEM_BUCKET_NAME_BASE, key);
    }

    /**
     * 根据Key删除文件
     * 
     * @param bucketName 服务器文件路径(一级目录)
     * @param key 密钥(上传后返回的字符串)
     * @return 成功还是失败(true:成功,false:失败)
     * @throws Exception
     */
    @Override
    public boolean remove(String bucketName, String key) throws Exception {
        return STORAGE_CLIENT.delete_file(bucketName, key) == 0;
    }

    /**
     * 从服务器下载文件到本地
     * 
     * @param key 密钥(上传后返回的字符串)
     * @return 
     * @throws Exception
     */
    @Override
    public boolean download(String key) throws Exception {
        return download(THIS_LOCALITY_FILE_SYSTEM_BUCKET_NAME_BASE, key, THIS_LOCALITY_DIRECTORY_BUCKET_NAME_BASE);
    }

    /**
     * 从服务器下载文件到本地
     * 
     * @param bucketName 服务器文件路径(一级目录)
     * @param key 密钥(上传后返回的字符串)
     * @return 
     * @throws Exception
     */
    @Override
    public boolean downloading(String bucketName, String key) throws Exception {
        return download(bucketName, key, THIS_LOCALITY_DIRECTORY_BUCKET_NAME_BASE);
    }

    /**
     * 从服务器下载文件到本地
     * 
     * @param key 密钥(上传后返回的字符串)
     * @param targetPath 本地文件路径
     * @return 
     * @throws Exception
     */
    @Override
    public boolean download(String key, String targetPath) throws Exception {
        return download(THIS_LOCALITY_FILE_SYSTEM_BUCKET_NAME_BASE, key, targetPath);
    }

    /**
     * 从服务器下载文件到本地
     * 
     * @param bucketName 服务器文件路径(一级目录)
     * @param key 密钥(上传后返回的字符串)
     * @param targetPath 本地文件路径
     * @return 
     * @throws Exception
     */
    @Override
    public boolean download(String bucketName, String key, String targetPath) throws Exception {
        return STORAGE_CLIENT.download_file(bucketName, key, targetPath) == 0;
    }

    /**
     * 根据Key获取文件流
     * 
     * @param key 密钥(上传后返回的字符串)
     * @return 文件字节流
     * @throws Exception
     */
    @Override
    public byte[] downloadGetBytes(String key) throws Exception {
        return downloadGetBytes(THIS_LOCALITY_FILE_SYSTEM_BUCKET_NAME_BASE, key);
    }

    /**
     * 根据Key获取文件流
     * 
     * @param bucketName 服务器文件路径(一级目录)
     * @param key 密钥(上传后返回的字符串)
     * @return 文件字节流
     * @throws Exception
     */
    @Override
    public byte[] downloadGetBytes(String bucketName, String key) throws Exception {
        return STORAGE_CLIENT.download_file(bucketName, key);
    }

    /**
     * 根据Key获取文件流
     * 
     * @param key 密钥(上传后返回的字符串)
     * @return 
     * @throws Exception
     */
    @Override
    public InputStream downloadGetInputStream(String key) throws Exception {
        return downloadGetInputStream(THIS_LOCALITY_FILE_SYSTEM_BUCKET_NAME_BASE, key);
    }

    /**
     * 根据Key获取文件流
     * 
     * @param bucketName 服务器文件路径(一级目录)
     * @param key 密钥(上传后返回的字符串)
     * @return 
     * @throws Exception
     */
    @Override
    public InputStream downloadGetInputStream(String bucketName, String key) throws Exception {
        return new ByteArrayInputStream(downloadGetBytes(bucketName, key));
    }

    /**
     * 获取连接
     * 
     * @return
     * @throws IOException 
     */
    @SuppressWarnings("unused")
    private CustomStorageClient getConnection() throws IOException {
        return new CustomStorageClient(TRACKER.getConnection(), null);
    }

    /**
     * 关闭连接
     * 
     * @param conn
     * @throws IOException 
     */
    @SuppressWarnings("unused")
    private void close(TrackerServer conn) throws IOException {
        // 关闭连接
        if (StringUtil.isNotBlankObject(conn)) {
            conn.close();
        }
    }

    /**
     * 根据Key获取Url
     * 
     * @param key 密钥(上传后返回的字符串)
     * @return 得到的Url
     * @throws Exception
     */
    @Override
    public String getUrl(String key) throws NoSuchAlgorithmException, MyException, IOException {
        return getUrl(THIS_LOCALITY_FILE_SYSTEM_BUCKET_NAME_BASE, key);
    }

    /**
     * 根据Key获取Url
     * 
     * @param bucketName 服务器文件路径(一级目录)
     * @param key 密钥(上传后返回的字符串)
     * @return 得到的Url
     * @throws Exception
     */
    @Override
    public String getUrl(String bucketName, String key) throws IOException, NoSuchAlgorithmException, MyException {

        StringBuilder sb = new StringBuilder(AGREEMENT);
        sb.append(URL);
        sb.append("/");
        sb.append(bucketName);
        sb.append("/");
        sb.append(key);

        return sb.toString();
    }

    /**
     * 根据Key获取Url(临时路径)
     * 
     * @param key 密钥(上传后返回的字符串)
     * @return 得到的Url
     * @throws Exception 
     */
    @Override
    public String getUrlTemporary(String key) throws Exception {
        return getUrlTemporary(THIS_LOCALITY_FILE_SYSTEM_BUCKET_NAME_BASE, key, ConstantByMap.FILE_URL_TIMEOUT);
    }

    /**
     * 根据Key获取Url(临时路径)
     * 
     * @param key 密钥(上传后返回的字符串)
     * @param timeout 地址超时时间(单位: 毫秒)
     * @return 得到的Url
     * @throws Exception 
     */
    @Override
    public String getUrlTemporary(String key, long timeout) throws Exception {
        return getUrlTemporary(THIS_LOCALITY_FILE_SYSTEM_BUCKET_NAME_BASE, key, timeout);
    }

    /**
     * 根据Key获取Url(临时路径)
     * 
     * @param bucketName 服务器文件路径(一级目录)
     * @param key 密钥(上传后返回的字符串)
     * @return 得到的Url
     * @throws Exception 
     */
    @Override
    public String getUrlTemporary(String bucketName, String key) throws Exception {
        return getUrlTemporary(bucketName, key, ConstantByMap.FILE_URL_TIMEOUT);
    }

    /**
     * 根据Key获取Url(临时路径)
     * 
     * @param bucketName 服务器文件路径(一级目录)
     * @param key 密钥(上传后返回的字符串)
     * @param timeout 地址超时时间(单位: 毫秒)
     * @return 得到的Url
     * @throws Exception 
     */
    @Override
    public String getUrlTemporary(String bucketName, String key, long timeout) throws Exception {
        // unix时间戳 以秒为单位
        int ts = (int)(System.currentTimeMillis() / 1000);

        StringBuilder sb = new StringBuilder(AGREEMENT);
        sb.append(URL);
        sb.append("/");
        sb.append(bucketName);
        sb.append("/");
        sb.append(key);
        sb.append("?");
        sb.append("token=").append(ProtoCommon.getToken(key, ts, secretKey));
        sb.append("&ts=").append(ts);

        return sb.toString();
    }

    public static String[] upload(byte[] bytes, String suffix, NameValuePair[] nvp) throws IOException, MyException {
        String[] fileIds = null;
        // NameValuePair nvp = new NameValuePair("age", "18");
        // NameValuePair nvp[] = new NameValuePair[] { new NameValuePair("age",
        // "18"),new NameValuePair("sex", "male") };
        // NameValuePair nvp = new NameValuePair("age", "18");
        fileIds = STORAGE_CLIENT.upload_file(bytes, suffix, nvp);
        // System.out.println(fileIds.length);
        // System.out.println("组名:" + fileIds[0]);
        // System.out.println("路径: " + fileIds[1]);
        return fileIds;
    }

    public static FileInfo getFileInfo(String path) throws IOException, MyException {
        FileInfo fi = null;
        fi = STORAGE_CLIENT.get_file_info(THIS_LOCALITY_FILE_SYSTEM_BUCKET_NAME_BASE, path);
        // 得到问价来源地址（存放地址）
        // System.out.println(fi.getSourceIpAddr());
        // 得到文件大小
        // System.out.println(fi.getFileSize());
        // 得到文件创建日期
        // System.out.println(fi.getCreateTimestamp());
        // 得到不知道，没查
        // System.out.println(fi.getCrc32());
        return fi;
    }

    public static NameValuePair[] getFileMate(String path) throws IOException, MyException {
        NameValuePair[] nvps = null;
        nvps = STORAGE_CLIENT.get_metadata(THIS_LOCALITY_FILE_SYSTEM_BUCKET_NAME_BASE, path);
        // for (NameValuePair nvp : nvps) {
        // System.out.println(nvp.getName() + ":" + nvp.getValue());
        // }
        return nvps;
    }

    public static FastDfsUtilImpl getInstances() {
        if (StringUtil.isBlankObject(fastDFSUtilsImpl)) {
            fastDFSUtilsImpl = new FastDfsUtilImpl();
        }
        return fastDFSUtilsImpl;
    }

    private class CustomStorageClient extends StorageClient {

        public CustomStorageClient(TrackerServer trackerServer, StorageServer storageServer) {
            super(trackerServer, storageServer);
        }

        public String[] upload(String groupName, String localFilename, String fileExtName, NameValuePair[] metaList)
            throws IOException, MyException {
            return upload_file(groupName, localFilename, fileExtName, metaList);
        }

    }

}